﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace Maniac
{
    /// <summary>
    /// A DayAggregate represents the summarized values of records for a given day. The daytype identifies the type of day this record corresponds to.
    /// </summary>
    public class DayAggregate
    {

        public DayAggregate()
        {
            this.iviHistoricalPrices = new List<HistoricalPrice>();
        }
        private int iviMonth;
        [CategoryAttribute("Date")]
        public int Month
        {
            get
            {
                return iviMonth;
            }
            set
            {
                iviMonth = value;
            }
        }

        private int iviDay;
        [CategoryAttribute("Date")]
        public int Day
        {
            get
            {
                return iviDay;
            }
            set
            {
                iviDay = value;
            }
        }

        public DayAggregate Clone()
        {
            DayAggregate tvTemp = new DayAggregate();
            //tvTemp.AverageGainPercent = AverageGainPercent;
            tvTemp.Symbol = Symbol;
            tvTemp.HistoricalPrices = this.HistoricalPrices;
            //tvTemp.AverageGain = AverageGain;
            //tvTemp.NumFalling = NumFalling;
            //tvTemp.NumRising = NumRising;
            return tvTemp;
        }

        private decimal? ivdAveragePrice;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The average price of the security on this day."), DisplayName("Average Price")]
        public decimal AveragePrice
        {
            get
            {
                if (!ivdAveragePrice.HasValue)
                {
                    decimal tvdTempAvgPrice = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        tvdTempAvgPrice += tvhPrice.ClosePrice;
                    }
                    ivdAveragePrice = Math.Round(tvdTempAvgPrice / HistoricalPrices.Count, 2);
                }                
                return ivdAveragePrice.Value;
            }
            set
            {
                ivdAveragePrice = Math.Round(value, 2);
            }
        }

        private decimal? iviNumRising;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The number of sessions where the security gained on this day."), DisplayName("Number of Gains")]
        public decimal TotalRising
        {
            get
            {
                if (!iviNumRising.HasValue)
                {
                    decimal tvdTempNum = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        bool tvbRise = (tvhPrice.GainPriorClose > 0) ? true : false;

                        if (tvbRise)
                            tvdTempNum++;
                    }
                    iviNumRising = tvdTempNum;
                }
                return iviNumRising.Value;                
            }
            set
            {
                iviNumRising = value;
            }
        }

        private decimal? iviNumFalling;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The number of sessions where the security declined on this day."), DisplayName("Number of Losses")]
        public decimal TotalFalling
        {
            get
            {
                if (!iviNumFalling.HasValue)
                {
                    decimal tvdTempNum = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        bool tvbRise = (tvhPrice.GainPriorClose < 0) ? true : false;

                        if (tvbRise)
                            tvdTempNum++;
                    }
                    iviNumFalling = tvdTempNum;
                }
                return iviNumFalling.Value;
            }
            set
            {
                iviNumFalling = value;
            }
        }

        private decimal? ivdMaxGain;
        [CategoryAttribute("Gains"), DescriptionAttribute("On this day, the maximum gain amount occurred for this date."), DisplayName("Maximum Gain")]
        public decimal MaxGain
        {
            get
            {
                if (!ivdMaxGain.HasValue)
                {
                    ivdMaxGain = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        if (tvhPrice.GainPriorClose > ivdMaxGain)
                        {
                            ivdMaxGain = tvhPrice.GainPriorClose;
                            ivdMaxGainDate = new DateTime(tvhPrice.Year, tvhPrice.Month, tvhPrice.Day);
                        }
                    }
                }
                return ivdMaxGain.Value;
            }
            set
            {
                ivdMaxGain = Math.Round(value, 2);
            }
            
        }

        private decimal? iviMaxLoss;
        [CategoryAttribute("Losses"), DescriptionAttribute("On this day, the maximum loss amount occurred for this date."), DisplayName("Maximum Loss")]
        public decimal MaxLoss
        {
            get
            {
                if (!iviMaxLoss.HasValue)
                {
                    iviMaxLoss = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        if (tvhPrice.GainPriorClose < iviMaxLoss)
                        {
                            iviMaxLoss = tvhPrice.GainPriorClose;
                            ivdMaxLossDate = new DateTime(tvhPrice.Year, tvhPrice.Month, tvhPrice.Day);
                        }
                    }
                }                
                return iviMaxLoss.Value;
            }
            set
            {
                iviMaxLoss = Math.Round(value, 2);
            }
        }

        private int iviOrdinalPosition;
        [BrowsableAttribute(false)]
        public int OrdinalPosition
        {
            get
            {
                return iviOrdinalPosition;
            }
            set
            {
                iviOrdinalPosition = value;
            }
        }
        private string ivsSymbol;
        [BrowsableAttribute(false)]
        public string Symbol
        {
            get
            {
                return ivsSymbol;
            }
            set
            {
                ivsSymbol = value;
            }
        }

        private decimal? ivdMaxGainPercent;
        [CategoryAttribute("Gains"), DescriptionAttribute("On this date, the maximum gain percent occurred for this day."), DisplayName("Maximum Gain (Percent)")]
        public decimal MaxGainPercent
        {
            get
            {
                if (!ivdMaxGainPercent.HasValue)
                {
                    ivdMaxGainPercent = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        if (tvhPrice.GainPriorClosePercent > ivdMaxGainPercent)
                        {
                            ivdMaxGainPercent = tvhPrice.GainPriorClosePercent;
                            ivdMaxGainPercentDate = new DateTime(tvhPrice.Year, tvhPrice.Month, tvhPrice.Day);
                        }
                    }
                }                
                return ivdMaxGainPercent.Value;
            }
            set
            {
                ivdMaxGainPercent = Math.Round(value, 2);
            }
        }

        private decimal? iviMaxLossPercent;
        [CategoryAttribute("Losses"), DescriptionAttribute("On this date, the maximum loss percent occurred for this day."), DisplayName("Maximum Loss (Percent)")]
        public decimal MaxLossPercent
        {
            get
            {
                if (!iviMaxLossPercent.HasValue)
                {
                    iviMaxLossPercent = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        if (tvhPrice.GainPriorClosePercent < iviMaxLossPercent)
                        {
                            iviMaxLossPercent = tvhPrice.GainPriorClosePercent;
                            ivdMaxLossPercentDate = new DateTime(tvhPrice.Year, tvhPrice.Month, tvhPrice.Day);
                        }
                    }
                }                
                return iviMaxLossPercent.Value;
            }
            set
            {
                iviMaxLossPercent = Math.Round(value, 2);
            }
        }

        private DateTime ivdMaxGainDate;
        [CategoryAttribute("Gains"), DescriptionAttribute("On this date, the maximum gain occurred for this day."), DisplayName("Maximum Gain Date")]
        public DateTime MaxGainDate
        {
            get
            {   
                this.MaxGain.GetType();
                return ivdMaxGainDate;
            }
            set
            {
                ivdMaxGainDate = value;
            }
        }

        private DateTime ivdMaxGainPercentDate;
        [CategoryAttribute("Gains"), DescriptionAttribute("On this date, the maximum gain percent occurred for this day."), DisplayName("Maximum Gain (Percent) Date")]
        public DateTime MaxGainPercentDate
        {
            get
            {
                this.MaxGainPercent.GetType();
                return ivdMaxGainPercentDate;
            }
            set
            {
                ivdMaxGainPercentDate = value;
            }
        }

        private DateTime ivdMaxLossDate;
        [CategoryAttribute("Losses"), DescriptionAttribute("On this date, the maximum loss occurred for this day."), DisplayName("Maximum Loss Date")]
        public DateTime MaxLossDate
        {
            get
            {
                this.MaxLoss.GetType();
                return ivdMaxLossDate;
            }
            set
            {
                ivdMaxLossDate = value;
            }
        }

        private DateTime ivdMaxLossPercentDate;
        [CategoryAttribute("Losses"), DescriptionAttribute("On this date, the maximum loss percent occurred for this day."), DisplayName("Maximum Loss (Percent) Date")]
        public DateTime MaxLossPercentDate
        {
            get
            {
                this.MaxLossPercent.GetType();
                return ivdMaxLossPercentDate;
            }
            set
            {
                ivdMaxLossPercentDate = value;
            }
        }
		private bool? ivbIsExDividendDate;
		[BrowsableAttribute(true)]
		public bool IsExDividendDate
		{
			get
			{
				if (!ivbIsExDividendDate.HasValue)
                {
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        if ((tvhPrice.Year == DateTime.Now.Year) &&(tvhPrice.IsExDividendDate))
                        {
                            ivbIsExDividendDate = true;
                            return ivbIsExDividendDate.Value;
                        }
                    }
                    ivbIsExDividendDate = false;
                }                
				return ivbIsExDividendDate.Value;
			}
			set
			{
				ivbIsExDividendDate = value;
			}
		}
        private decimal? ivdMostRecentActualGain;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The most recent actual gain or loss on this day."), DisplayName("Most Recent Gain Amount")]
        public decimal MostRecentActualGain
        {
            get
            {
                if (!ivdMostRecentActualGain.HasValue)
                {
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        if (tvhPrice.Year == DateTime.Now.Year)
                        {
                            ivdMostRecentActualGain = tvhPrice.GainPriorClose;
                            ivdMostRecentActualGainPercent = tvhPrice.GainPriorClosePercent;
                            return ivdMostRecentActualGain.Value;
                        }
                    }
                    ivdMostRecentActualGain = 0;
                    ivdMostRecentActualGainPercent = 0;
                }
                return ivdMostRecentActualGain.Value;
            }
            set
            {
                ivdMostRecentActualGain = Math.Round(value, 2);
            }
        }

        private decimal ivdMostRecentActualGainPercent;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The most recent actual percentage gain or loss on this day."), DisplayName("Most Recent Gain (Percent)")]
        public decimal MostRecentActualGainPercent
        {
            get
            {
                this.MostRecentActualGain.GetType();
                return ivdMostRecentActualGainPercent;
            }
            set
            {
                ivdMostRecentActualGainPercent = Math.Round(value, 2);
            }
        }
        private decimal? tvdAverageGain;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The average gain amount on this day."), DisplayName("Average Gain (Intraday)")]
        public decimal AverageGain
        {
            get
            {
                if (!tvdAverageGain.HasValue)
                {
                    decimal tvdTempAvgGain = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        tvdTempAvgGain += tvhPrice.GainIntraday;
                    }
                    tvdAverageGain = Math.Round(tvdTempAvgGain / HistoricalPrices.Count, 2);
                }
                return tvdAverageGain.Value;
            }
            set
            {
                tvdAverageGain = Math.Round(value, 2);
            }
        }
        private decimal? tvdAverageGainPercent;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The average gain percent on this day."), DisplayName("Average Gain (Intraday Percent)")]
        public decimal AverageGainPercent
        {
            get
            {
                if (!tvdAverageGainPercent.HasValue)
                {
                    decimal tvdTempAvgGain = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        tvdTempAvgGain += tvhPrice.GainIntradayPercent;
                    }
                    tvdAverageGainPercent = Math.Round(tvdTempAvgGain / HistoricalPrices.Count, 2);
                }
                return tvdAverageGainPercent.Value;
            }
            set
            {
                tvdAverageGainPercent = Math.Round(value, 2);
            }
        }
        private decimal? tvdAverageGainPriorClose;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The average gain amount on this day compared with the prior close."), DisplayName("Average Gain (Prior Close)")]
        public decimal AverageGainPriorClose
        {
            get
            {
                if (!tvdAverageGainPriorClose.HasValue)
                {
                    decimal tvdTempAvgGain = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        tvdTempAvgGain += tvhPrice.GainPriorClose;
                    }
                    tvdAverageGainPriorClose = Math.Round(tvdTempAvgGain / HistoricalPrices.Count, 2);
                }
                return tvdAverageGainPriorClose.Value;
            }
            set
            {
                tvdAverageGainPriorClose = Math.Round(value, 2);
            }
        }
        private decimal? tvdAverageGainPercentPriorClose;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The average gain percent on this day compared with the prior close."), DisplayName("Average Gain (Prior Close Percent)")]
        public decimal AverageGainPercentPriorClose
        {
            get
            {
                if (!tvdAverageGainPercentPriorClose.HasValue)
                {
                    decimal tvdTempAvgGain = 0;
                    foreach (HistoricalPrice tvhPrice in HistoricalPrices)
                    {
                        tvdTempAvgGain += tvhPrice.GainPriorClosePercent;
                    }
                    tvdAverageGainPercentPriorClose = Math.Round(tvdTempAvgGain / HistoricalPrices.Count, 2);
                }
                return tvdAverageGainPercentPriorClose.Value;
            }
            set
            {
                tvdAverageGainPercentPriorClose = Math.Round(value, 2);
            }
        }

        private List<HistoricalPrice> iviHistoricalPrices;
        [BrowsableAttribute(false)]
        public List<HistoricalPrice> HistoricalPrices
        {
            get
            {
				if(iviHistoricalPrices.Count==0)
				{
					Console.WriteLine("Getting Historical Prices for " + this.Symbol);
					DateTime dtNow = DateTime.Now;
					DataTier.GetHistoricalPrices(iviHistoricalPrices, this.Symbol, this.Day, this.Month);
					Console.WriteLine("Got Historical Prices: " + (DateTime.Now-dtNow));
				}
                return iviHistoricalPrices;
            }
            set
            {
                //done for cloning.
                iviHistoricalPrices = value;
            }
        }

        private decimal? tvdGainVariance;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The gain variance on this day."), DisplayName("Gain Variance (Prior Close)")]
        public decimal GainVariance
        {
            get
            {
                if (!tvdGainVariance.HasValue)
                {
                    decimal tvdSum = 0;
                    foreach (HistoricalPrice tvhPrice in this.HistoricalPrices)
                    {
                        tvdSum+=Convert.ToDecimal(Math.Pow(Convert.ToDouble(tvhPrice.GainPriorClose-this.AverageGain),2));
                    }                    
                    tvdGainVariance = Math.Round(tvdSum / HistoricalPrices.Count, 2);
                }
                return tvdGainVariance.Value;
            }
            set
            {
                tvdGainVariance = Math.Round(value, 2);
            }
        }

        private decimal? tvdGainStdDeviation;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The gain standard deviation on this day."), DisplayName("Gain Standard Deviation (Prior Close)")]
        public decimal GainStandardDeviation
        {
            get
            {
                if (!tvdGainStdDeviation.HasValue)
                {
                    tvdGainStdDeviation = Math.Round(Convert.ToDecimal(Math.Sqrt(Convert.ToDouble(this.GainVariance))), 2);
                }
                return tvdGainStdDeviation.Value;
            }
            set
            {
                tvdGainStdDeviation = Math.Round(value, 2);
            }
        }

        private decimal? tvdGainPercentVariance;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The gain percent variance on this day."), DisplayName("Gain Percent Variance (Prior Close)")]
        public decimal GainPercentVariance
        {
            get
            {
                if (!tvdGainPercentVariance.HasValue)
                {
                    decimal tvdSum = 0;
                    foreach (HistoricalPrice tvhPrice in this.HistoricalPrices)
                    {
                        tvdSum += Convert.ToDecimal(Math.Pow(Convert.ToDouble(tvhPrice.GainPriorClosePercent - this.AverageGainPercentPriorClose), 2));
                    }
                    tvdGainPercentVariance = Math.Round(tvdSum / HistoricalPrices.Count, 2);
                }
                return tvdGainPercentVariance.Value;
            }
            set
            {
                tvdGainPercentVariance = Math.Round(value, 2);
            }
        }


        private decimal? tvdGainPercentStdDeviation;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The gain percent standard deviation on this day."), DisplayName("Gain Percent Standard Deviation (Prior Close)")]
        public decimal GainPercentStandardDeviation
        {
            get
            {
                if (!tvdGainPercentStdDeviation.HasValue)
                {
                    tvdGainPercentStdDeviation = Math.Round(Convert.ToDecimal(Math.Sqrt(Convert.ToDouble(this.GainPercentVariance))), 2);
                }
                return tvdGainPercentStdDeviation.Value;
            }
            set
            {
                tvdGainPercentStdDeviation = Math.Round(value, 2);
            }
        }

        private decimal? tvdGainPercentStdDeviationDelta;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The difference between the average percentage gain and the gain percent standard deviation on this day."), DisplayName("Gain Percent Standard Deviation Delta (Prior Close)")]
        public decimal GainPercentStandardDeviationDelta
        {
            get
            {
                if (!tvdGainPercentStdDeviationDelta.HasValue)
                {
                    tvdGainPercentStdDeviationDelta = Math.Abs(this.AverageGainPercentPriorClose)-Math.Abs(this.GainPercentStandardDeviation);
                }
                return tvdGainPercentStdDeviationDelta.Value;
            }
            set
            {
                tvdGainPercentStdDeviationDelta = Math.Round(value, 2);
            }
        }        
        private decimal? tvdProbability;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The probability of this security rising on this day."), DisplayName("Rise Probability (Prior Close)")]
        public decimal RiseProbability
        {
            get
            {
                if (!tvdProbability.HasValue)
                {
                    if ((this.TotalFalling + this.TotalRising) > 0)
                        tvdProbability = (this.TotalRising * 100 / (this.TotalRising + this.TotalFalling));
                    else
                        tvdProbability = 0;

                }
                return Math.Round(tvdProbability.Value,2);
            }
            set
            {
                tvdProbability = Math.Round(value, 2);
            }
        }


        private decimal? tvdGainMedian;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The median gain on this day."), DisplayName("Gain Median (Prior Close)")]
        public decimal GainMedian
        {
            get
            {
                if (!tvdGainMedian.HasValue)
                {
                    List<HistoricalPrice> tvhTemp = HistoricalPrices.GetRange(0, HistoricalPrices.Count);
                    tvhTemp.Sort(HistoricalPriceSorter.SortPricesByGainPriorClose);
                    int tviSize = tvhTemp.Count - 1;
                    int tviMid = tviSize / 2;
                    if ((tviSize % 2) != 0)
                    {                        
                        tvdGainMedian = tvhTemp[tviMid].GainPriorClose;
                    }
                    else
                    {
                        if (tviMid>0)
                            tvdGainMedian = (tvhTemp[tviMid].GainPriorClose+tvhTemp[tviMid-1].GainPriorClose)/2;
                        else
                            tvdGainMedian = tvhTemp[tviMid].GainPriorClose;
                    }
                }
                return Math.Round(tvdGainMedian.Value,2);
            }
            set
            {
                tvdGainMedian = Math.Round(value, 2);
            }
        }
        private decimal? tvdGainPercentMedian;
        [CategoryAttribute("Metrics"), DescriptionAttribute("The median gain percent on this day."), DisplayName("Gain Median (Prior Close Percent)")]
        public decimal GainPercentMedian
        {
            get
            {
                if (!tvdGainPercentMedian.HasValue)
                {
                    List<HistoricalPrice> tvhTemp = HistoricalPrices.GetRange(0, HistoricalPrices.Count);
                    tvhTemp.Sort(HistoricalPriceSorter.SortPricesByGainPercentPriorClose);
                    int tviSize = tvhTemp.Count - 1;
                    int tviMid = tviSize / 2;
                    if ((tviSize % 2) != 0)
                    {
                        tvdGainPercentMedian = tvhTemp[tviMid].GainPriorClosePercent;
                    }
                    else
                    {
                        tvdGainPercentMedian = (tvhTemp[tviMid].GainPriorClosePercent + tvhTemp[tviMid - 1].GainPriorClosePercent) / 2;
                    }
                }
                return tvdGainPercentMedian.Value;
            }
            set
            {
                tvdGainPercentMedian = Math.Round(value, 2);
            }
        }
       /* private static int SortPricesByGainPriorClose(HistoricalPrice x, HistoricalPrice y)
        {
            int tviReturn = 0;
            if (x == null)
            {
                if (y == null)
                {
                    // If x is null and y is null, they're
                    // equal. 
                    return 0;
                }
                else
                {
                    // If x is null and y is not null, y
                    // is greater. 
                    return -1;
                }
            }
            else
            {
                // If x is not null...
                //
                if (y == null)
                // ...and y is null, x is greater.
                {
                    return 1;
                }
                else
                {
                    // ...and y is not null, compare the 
                    // lengths of the two strings.
                    //

                    if (x.GainPriorClose > y.GainPriorClose)
                        return -1;
                    if (x.GainPriorClose == y.GainPriorClose)
                        return 0;
                    if (x.GainPriorClose < y.GainPriorClose)
                        return 1;

                }
            }

            return tviReturn;
        }*/
        /*private static int SortPricesByGainPercentPriorClose(HistoricalPrice x, HistoricalPrice y)
        {
            int tviReturn = 0;
            if (x == null)
            {
                if (y == null)
                {
                    // If x is null and y is null, they're
                    // equal. 
                    return 0;
                }
                else
                {
                    // If x is null and y is not null, y
                    // is greater. 
                    return -1;
                }
            }
            else
            {
                // If x is not null...
                //
                if (y == null)
                // ...and y is null, x is greater.
                {
                    return 1;
                }
                else
                {
                    // ...and y is not null, compare the 
                    // lengths of the two strings.
                    //

                    if (x.GainPriorClosePercent > y.GainPriorClosePercent)
                        return -1;
                    if (x.GainPriorClosePercent == y.GainPriorClosePercent)
                        return 0;
                    if (x.GainPriorClosePercent < y.GainPriorClosePercent)
                        return 1;

                }
            }

            return tviReturn;
        }
         * */
    }
}
